using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using Geosoft.GXNet;
using Geosoft.GX.Controls;

// Please retain the following line to make future critical updates to GXs based on old templates easier
// CREATED FROM: Geosoft Simple GX Template version 1.0 

// README FIRST!
//
// TODO: Search for all the occurances of "TODO:" below and follow the instructions to get
//       your GX up and running quickly. You can delete the TODOs as you complete/read them 
//       to clean up the source. 
//
//       Once you are familiar enough with the workflow of GX creation you can make a new form
//       from this template, cut out all the TODO: comments you want and make your own streamlined
//       template using the File->Export Template... menu option in Visual Studio.


// TODO: Chooses a namespace to use below. For instance: 
//       Geosoft prefixes all their GX .Net GXs' namespaces with
//       Geosoft.GX.... followed by a functional grouping.
namespace SpecGX
{
    // TODO: 1. Describe your new class below in the summary. You can add extra notes about the class in the remarks section.
    //       
    //       2. Now may also be a good oppurtunity to set the following properties in the designer:
    //
    //             GXParameterGroup: Will be used to remember previous settings for this form in montaj.
    //             HelpTopicID:      If used or set the HelpButton property to false to not display the 
    //                               button in the titlebar.
    //
    //
    //       3. Add GX Controls and any other needed Windows forms controls from the toolbox to the form sizing 
    //          and positioning everything as needed. Give each control a funtional name indicating that it is a
    //          member of the form and what kind of control it is (e.g. m_cmbAssayList for a combobox containing a 
    //          list of assay options). This makes for readable and maintainable code.
    //
    //       4. For every GX Control added inspect the GX_Parameters group in the Properties window for the control 
    //          to edit any needed properties. At the very least either the GXParameterName or the UseGXParams property
    //          might need to be set. Document any scripting parameters in the XML documentation below, e.g.:
    //
    //          /// <scriptparam group="GXPARAMGROUP" name="GXPARAM">This parameter is the example parameter.</scriptparam>
    //
    /// <summary>
    /// 
    /// </summary>
    /// <remarks>
    /// 
    /// </remarks>
    public partial class GX1 : BaseForm
    {
        // TODO: The code in this region can generally be left alone unless you needed to do special
        //       management and cleanup of resources.
        #region Constructors/Destructors
        /// <summary>
        /// Default constructor
        /// </summary>
        /// <remarks>
        /// Use this constructor for instantiating this class from within other GX .Net based forms
        /// </remarks>
        public GX1()
            : base()
        {
            // This call is required by the Windows.Forms Form Designer.
            InitializeComponent();
        }

        /// <summary>
        /// Constructor with GX handle
        /// </summary>
        /// <param name="iGeo">Geosoft handle from montaj with which to initialize the BaseForm</param>
        /// <remarks>
        /// This constructor will be used if this GX is instantiated from within montaj (e.g. a menu entry)
        /// </remarks>
        public GX1(int iGeo)
            : base(iGeo)
        {
            InitializeComponent();
        }

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            // TODO: If any resources are used which may need cleanup at the end of GX execution this is the place to
            //       clean it up. This can include locked GX objects, files, child forms etc. For example:
            //
            // if (m_dlgPreview != null)
            //    m_dlgPreview.Dispose();
            // m_dlgPreview = null;
            // 
            // if (m_strTempFile.Length > 0 && File.Exists(m_strTempFile))
            //    File.Delete(m_strTempFile);

            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }
        #endregion Constructors/Destructors

        // TODO: The overrided methods in the region below does any extra work needed during the various 
        //       parts of the GX flow (initialization, validation etc.) which cannot be provided by the controls 
        //       alone. Some controls will validate themselves without having to add any extra code here and where
        //       possible that functionality should be used without having to add any code here (e.g. the Required 
        //       property, or the MaximumValue on the EditIntControl).
        //
        // NOTE: Do not use a message box in these overrides if you intend to use the GX in the non-interactive
        //       scripting environment. The use of CSYS.Abort, CSYS.DisplayMessage etc. are preferred.
        #region BaseForm Overrides
        /// <summary>
        /// Override to do extra initialization (combobox lst init etc.)
        /// </summary>
        /// <remarks>The derived class should call this function in the appropriate place</remarks>
        protected override void InitializeForm()
        {
            base.InitializeForm();

            // TODO: If needed add extra initialization/pre-validation here, for example:
            //
            //
            // CEDB hEDB = null;
            // CDB hDB = null;
            // try
            // {
            //    hEDB = CEDB.Current();
            //    hDB = hEDB.Lock();
            //    CLST hChanLST = CLST.Create(128);
            //    hDB.ArrayLST(hChanLST);
            //
            //    if (hChanLST.iSize() < 2)
            //       CSYS.Abort("At least two array channels are required.");
            //    m_cmbData.Init(hChanLST, Constant.LST_ITEM_NAME);
            // }
            // finally
            // {
            //    if (hDB == null) 
            //       hEDB.UnLock();
            // }
        }

        /// <summary>
        /// Override to do extra validation
        /// </summary>
        /// <param name="strINIError">
        /// </param>
        /// <param name="bUseErrorProvider">
        /// </param>
        /// <returns>true if validation is successful</returns>
        public override bool bValidateDialog(ref string strINIError, bool bUseErrorProvider)
        {
            bool bValid = true;

            // Do base dialog validation first (this will validate all controls based on their properties first)

            if (!base.bValidateDialog(ref strINIError, bUseErrorProvider))
                bValid = false;

            if (bValid)
            {
                // TODO: Add any more complex validation logic here. Not that 3 things need to happen for validation failures:
                //
                //       1. bValid should be set to false.
                //       2. Create a string describing the problem and if the bUseErrorProvider parameter is true set this
                //          error and the control in question to the base class' ControlErrorProvider.SetError method.
                //       3. Append an error string to the strINIError string parameter decribing the problem. The convenient 
                //          FormatINIErrorString of the GX Controls may be used if the problem lies in a specific control,
                //          This ensures logical errors are reported during scripting of GXs.
                //       
                //       For Example:
                //
                // 
                // int iNGrids = m_EditNumGrids.iGetValue();
                // double dStartElev = m_EditStartElev.dGetValue();
                // double dElevInc = m_EditElevInc.dGetValue();
                // 
                // if (iNGrids != Constant.iDUMMY && dStartElev == Constant.rDUMMY)
                // {
                //    bValid = false;
                //    strError = "The starting elevation must be defined if the number of grids is defined.";
                //    if (bUseErrorProvider)
                //       this.ControlErrorProvider.SetError(m_EditStartElev, strError);
                //    strINIError += m_EditStartElev.FormatINIErrorString(strError);
                // }
                // if (iNGrids > 1 && dElevInc == Constant.rDUMMY)
                // {
                //    bValid = false;
                //    strError = "The elevation increment must be defined if the number of grids is defined and is greater 1.";
                //    if (bUseErrorProvider)
                //       this.ControlErrorProvider.SetError(m_EditElevInc, strError);
                //    strINIError += m_EditElevInc.FormatINIErrorString(strError);
                // }
                // else if (dElevInc == 0.0 && iNGrids > 1)
                // {
                //    bValid = false;
                //    strError = "The elevation increment must not be zero if the number of grids is defined and is greater 1.";
                //    if (bUseErrorProvider)
                //       this.ControlErrorProvider.SetError(m_EditElevInc, strError);
                //    strINIError += m_EditElevInc.FormatINIErrorString(strError);
                // }
            }
            return bValid;
        }

        /// <summary>
        /// Set all the controls information needed to recreate the GX's state to the INI
        /// </summary>
        /// <remarks>
        /// Will be called just before dialog returns <see cref="System.Windows.Forms.DialogResult.OK"/>
        /// in interactive mode. Override to set extra info needed for scripting etc, but be sure to 
        /// call the base class first.
        /// </remarks>
        public override void SetToINI()
        {
            // --- Call base class first ---
            base.SetToINI();

            // TODO: If needed add extra initialization/pre-validation here, for example:
            //
            // CSYS.SetInt(this.GXParameterGroup, "TRANSPARENCY", m_trackBar.Value);
        }
        #endregion

        // TODO: This region contains the first method called by montaj after instantiating the GX.
        //       In general the code inside may be left as is.
        #region Entry Point Method
        /// <summary>
        /// Run the GX.
        /// </summary>
        [CGXAttribute("")]
        public void Run()
        {
            DialogResult Result = DialogResult.OK;

            try
            {
                // Initialize the form
                InitializeForm();

                // Are we running interactively?

                if (CSYS.iInteractive() != 0)
                    Result = this.ShowDialog();
                else
                    NonInteractiveSetFromINI(); // Validation and defaults picked up here
            }
            catch (ApplicationException e)
            {
                // An empty message indicates a CSYS.Cancel()
                if (e.Message.Length > 0)
                    CSYS.Abort(e.Message);
                else
                    Result = DialogResult.Cancel;
            }
            catch (Exception e)
            {
                CSYS.Abort("Failure in the \"" + this.Name + "\" GX.NET Form (" + e.Message + ")");
            }

            if (Result == DialogResult.OK)
            {
                try
                {
                    Execute();
                }
                catch (Exception e)
                {
                    // An empty message indicates a cancel
                    if (e.Message.Length > 0)
                        CSYS.Abort(e.Message);
                }
            }
        }
        #endregion Entry Point


        // TODO: This is where the processing happens in the GX and is probably where most of the real coding
        //       will happen. Some sample code below with comments may help you along here.
        #region Execute Method
        private void Execute()
        {
            // Always validate using this call first
            string strError = String.Empty;
            if (!bValidateDialog(ref strError, false))
                CSYS.Abort(strError);

            // TODO: Get the control values. The GX Controls internals would have already initialized 
            //       any controls and validation in the internals and overrides above would have already 
            //       taken place ath this point so in most case no further validation is needed.
            //       For example:

            // string strOption = String.Empty;
            // double dScale;
            //
            // dScale = m_RealScale.dGetValue();
            // ...
            // 


            // TODO: In some cases GXs behave differently in non-interactive modes and extra options can be passed,
            //       the code below is an example of how the getting of the values and validation should be handled in this case.
            //       For example:

            // if (CSYS.iInteractive() == 0)
            // {
            //    CSYS.GtString("GXPARAMGROUP", "NONINTERACTIVEOPTION", ref strOption);
            // 
            //    // Validate
            // 
            //    if (strOtion.Length == 0) CSYS.Abort("ArcGIS LYR file name not specified");
            // }
            // 


            // TODO: Do the work, cleaning up any resources used (try/finally).
            //       For example:

            try
            {
                CMAP hMap = null;
               
                CMVIEW hMView = null;
    
                string input_file_path = "";

                string XSize = " " + txtXSize.Text;
                string YSize = " " + txtYSize.Text;

                string start = " " + txtStart.Text;
                string end = " " + txtEnd.Text;

                string startColourRange = " " + txtStartColourRange.Text;
                string endColourRange = " " + txtEndColourRange.Text;

                string islinear = " " + chkLinear.Checked.ToString();

                if (lstInputFiles.Items.Count == 0)
                {
                    MessageBox.Show("Error: Input list empty. Add files to list.");
                    return;
                } 

                string geosoft_path = "";
                CSYS.IGetPath((int)Constant.SYS_PATH_GEOSOFT, ref geosoft_path);
                
                string output_dir = geosoft_path + "bin\\cl_spec\\temp";
                input_file_path = output_dir + "\\input_files.txt";

                //Write list of input files to file for spectrogram to read
                TextWriter writer = new StreamWriter(input_file_path);

                foreach (string file in lstInputFiles.Items) {
                    writer.WriteLine(file);
                }
                writer.Close();

                string arguments = "\""+input_file_path+"\"" + XSize + YSize + start + end + startColourRange + endColourRange + islinear + " \""+output_dir+"\"";
                
                // Run the external app with the textbox as parameter input
                MessageBox.Show(arguments);
                //int result = CSYS.iRun("P:\\software\\temp\\Coran\\Spectrogram GX\\cl_spec\\cl_spec.exe", arguments, Constant.SYS_RUN_WIN_WAIT);
                
                //MessageBox.Show(geosoft_path+ "bin\\cl_spec\\cl_spec.exe" + arguments);
                int result = CSYS.iRun(geosoft_path + "bin\\cl_spec\\cl_spec.exe", arguments, Constant.SYS_RUN_HOLD_ONERROR);

                //Use this line for debugging
                //MessageBox.Show("Result:" + result);
                

                if (result == 0) {

                    CEMAP hEMap = null; //debug
                    string map_file = "";

                    if (chkReplaceCurrentMap.Checked == true)
                    {
                        string new_map_name = "";

                        if (CEMAP.iHaveCurrent() == 0)
                        {
                            MessageBox.Show("Error: No open map for spectrogram to replace.");
                            return;
                        }
                        {
                            hEMap = CEMAP.Current();
                            hMap = hEMap.Lock();

                            //Open existing view to replace
                            hMView = CMVIEW.Create(hMap, "data", Constant.MVIEW_WRITEOLD);
                        }
                    }
                    else {
                        string path = geosoft_path + "bin\\cl_spec\\temp\\";// + txtNewMapName.Text + ".map";
                        map_file = path + txtNewMapName.Text + ".map";
                       
                        bool still_trying = true;
                        int suffix = 1;

                        while (still_trying) {
                            try
                            {
                                if (File.Exists(map_file))
                                {
                                    File.Delete(map_file);
                                    still_trying = false;
                                }
                                else {
                                    still_trying = false;
                                }
                            }
                            catch (IOException e)
                            {
                                map_file = path + txtNewMapName.Text + suffix + ".map";
                                suffix++;
                                continue;
                            }
                        }
                        
                        //Create new map
                        hMap = CMAP.Create(map_file, (int)Geosoft.GXNet.MAP_OPEN.WRITENEW);

                        //Create new data view in the new map
                        hMView = CMVIEW.Create(hMap, "data", Constant.MVIEW_WRITENEW);
                    }
                    //Create aggregate for the image
                    CAGG hAgg = CAGG.Create();

                    //Add image to the aggregate
                    hAgg.LayerIMG(geosoft_path + "bin\\cl_spec\\temp\\spectrogram.jpg(IMG;t=jpg)", 0, "", Constant.rDUMMY);

                    //Put aggregate into the view
                    hMView.Aggregate(hAgg, "Spectrogram");

                    hMView.Dispose();
                    hMap.Commit();
                   
                    //Dispose will also unlock the emap associated with it
                    hMap.Dispose();
                   
                    //Load newly created map into geosoft
                    if (chkReplaceCurrentMap.Checked == false) {
                        CEMAP eMap = CEMAP.Load(map_file);
                    }
                   
                    
                    // #### Unfinished enhancement code below for placement and sizing of spectrogram inside Geosoft upon placement ####
                    /*string strGroup = "";

                    Create a group within the view (unique string name)
                    hMView.IGenNewGroupName("Spectrogram", ref strGroup);

                    hMView.StartGroup(strGroup, (int)Geosoft.GXNet.MVIEW_GROUP.NEW);

                    hMView.RelocateGroup(strGroup, minX, minY, maxX, maxY, (int)Geosoft.GXNet.MVIEW_RELOCATE.FIT);

                    RelocateGroup_MVIEW(View, sAgg, rMinX, rMinY, rMaxX, rMaxY, IMG_RELOCATE_ASPECT);


                     --- clip the aggregate group ---

                    hMView.MarkAllGroups(0); // 0 = unmark
                    hMView.MarkGroup(strGroup, 1); // 1 = mark
                    hMView.ClipMarkedGroups((int)Geosoft.GXNet.MVIEW_CLIP.ON); //CLIP_ON
                    hMView.MarkAllGroups(0);

                    MarkAllGroups_MVIEW(View, 0);
                    MarkGroup_MVIEW(View, sAgg, 1);
                    ClipMarkedGroups_MVIEW(View, CLIP_ON);
                    MarkAllGroups_MVIEW(View, 0);*/

                }
                else {
                    switch(result) {
                            //This must be in step with the Python spectrogram script
                        case 1: MessageBox.Show("Internal error: See log file at c:\\temp\\cl_spec\\log.txt");
                            break;
                        case 2: MessageBox.Show("Too few paramaters in command call");
                            break;
                        case 3: MessageBox.Show("PSD file does not exist");
                            break;
                        case 4: MessageBox.Show("Invalid zero data values");
                            break;
                        case 5: MessageBox.Show("Incorrect time format");
                            break;
                        case 6: MessageBox.Show("No times found");
                            break;
                        case 7: MessageBox.Show("Incorrect line format");
                            break; 
                        default:
                            MessageBox.Show("Spectrogram creation failed with error code:" + result);
                            break;
                    }
                }
            

                //Unlock objects
                //hEDB.UnLock();

                //Release all objects
                //hMView.Dispose();
                //hEMap.Dispose();
               // hMap.Dispose();
                //hAgg.Dispose();

                

                // hEDB = CEDB.Current();
                // hDB = hEDB.Lock();
                // 
                // if (strOption.Length > 0)
                // {
                //    ...
                // }
                //
                // // Get and lock the X and Y channels
                // int iXCh = hDB.GetXYZChanSymb(Constant.DB_CHAN_X);
                // int iYCh = hDB.GetXYZChanSymb(Constant.DB_CHAN_Y);
                // if(iXCh == Constant.NULLSYMB)
                //    CSYS.Abort("The current X channel was not found in the current database.");
                // if(iYCh == Constant.NULLSYMB)
                //    CSYS.Abort("The current Y channel was not found in the current database.");
                // hDB.LockSymb(iXCh, Constant.DB_LOCK_READONLY, Constant.DB_WAIT_INFINITY);
                // hDB.LockSymb(iYCh, Constant.DB_LOCK_READONLY, Constant.DB_WAIT_INFINITY);
                // ....
            }
            finally
            {
                // if (hDB != null)
                // {
                //    hDB.UnLockAllSymb();
                //    hEDB.UnLock();
                // }
            }
        }
        #endregion Execute Method

        // TODO: Organize any event handlers added (either manually or through the designer) in the region below. Event
        //       handlers may be used to provide interactive changes to the form appearance based on user input which
        //       can deliver a more natural user experience (e.g. enabling/disabling controls based on reaction 
        //       to a SelectedValueChanged event from a ComboBoxControl).
        //
        //       Note: The default behaviour for the OK button has already been added. None is needed for the Cancel button
        //       in most cases.
        //
        //       Should any extra logic be needed it is generally a good idea to add it to the BaseForm overrides above 
        //       rather than inside m_btnOK_Click which will make things easier to maintain as well as maintain the ability 
        //       of the GX to run in non-interactive (scripting) mode and function logically.
        #region Event Handlers
        private void m_btnOK_Click(object sender, EventArgs e)
        {
            try
            {
                if (!bValidateDialog())
                {
                    // This to not close the dialog
                    DialogResult = DialogResult.None;
                    return;
                }

                // Set dialog control values back into the project INI
                SetToINI();
            }
            catch (Exception ex)
            {
                // Show errors and clear it so that it will not present again on GX exit
                if (ex.Message.Length > 0)
                    MessageBox.Show(ex.Message, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error);
                CSYS.iClearErrAP();
                // This to not close the dialog
                DialogResult = DialogResult.None;
            }
        }
        /// <summary>
        /// Insert the user selected file from the popup dialog into the control
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSelPSD_Click(object sender, EventArgs e)
        {
            
            OpenFileDialog fd = new OpenFileDialog();
            fd.Filter = "PSD Files|*.rsl|All files|*.*";
            fd.Multiselect = true;
            fd.InitialDirectory = txtInputPSD.Text;
            fd.RestoreDirectory = true;
            fd.ShowDialog();

            if (fd.FileNames.Length == 1){
                txtInputPSD.Text = fd.FileName;
                lstInputFiles.Items.Add(fd.FileName);
            }
            else foreach (string file in fd.FileNames)
            {
                txtInputPSD.Text = "";
                lstInputFiles.Items.Add(file);
            }
        }

        private void btnRemove_Click(object sender, EventArgs e)
        {
            lstInputFiles.Items.Remove(lstInputFiles.SelectedItem);
        }
        #endregion Event Handlers

        private void chkProcessRange_CheckedChanged(object sender, EventArgs e)
        {
            if (chkProcessRange.Checked == true)
            {
                txtStart.Enabled = true;
                txtEnd.Enabled = true;
            }
            else {
                txtStart.Enabled = false;
                txtEnd.Enabled = false;
            }
        }

        private void chkClampColourRange_CheckedChanged_1(object sender, EventArgs e)
        {
            if (chkClampColourRange.Checked == true)
            {
                txtStartColourRange.Enabled = true;
                txtEndColourRange.Enabled = true;
            }
            else
            {
                txtStartColourRange.Enabled = false;
                txtEndColourRange.Enabled = false;
            }
        }

        private void chkReplaceCurrentMap_CheckedChanged(object sender, EventArgs e)
        {
            //If replacing current map, disable new map name and auto-rename map UI
            if (chkReplaceCurrentMap.Checked == true) {
                txtNewMapName.Enabled = false;
            }
            else {
                //If auto renaming isnt enabled, enable the new map name text field
                txtNewMapName.Enabled = true;
            }
        }
    }
}